using System;
using System.Collections.Generic;

using UnityEngine;

namespace Game.Networking.Client {
	public class ClientEntityController : ClientsideController<ClientEntityController>, IEntityController {
		public ClientEntityController(ClientGameManager owner) : base(owner) { }

		Dictionary<uint, Entity> _entities = new Dictionary<uint, Entity>();
		float _lastTickTime = 0f;

		public override void Init() {
			base.Init();
		}

		public override void Update() {
			base.Update();
			var curTime = ClientGameManager.Time;
			var deltaTime = curTime - _lastTickTime;
			//TODO: update only active entities
			foreach ( var p in _entities ) {
				var e = p.Value;
				if ( e?.IsEntityAlive ?? false ) {
					e.Tick(curTime, deltaTime);
				}				
			}
			_lastTickTime = curTime;
		}

		public Entity CreateNewEntity(EntityState state, Action<Entity> setupAction = null) {
			if ( state == null ) {
				Debug.LogWarning("ClientEntityController.CreateNewEntity: no state provided.");
			}
			if ( state.ID == 0 ) {
				Debug.LogWarning("ClientEntityController.CreateNewEntity: trying to create entity with id 0. This is invalid");
			}
			if ( _entities.ContainsKey(state.ID ) ) {
				Debug.LogWarning($"ClientEntityController.CreateNewEntity: ID conflict: entity with id {state.ID} was already created. Replacing old entity with new.");
				DespawnEntity(state.ID);
			}

			var position = Vector3.zero;
			var rotation = Vector3.zero;
			var cast = (WorldEntityState)state;
			if ( cast != null ) {
				position = cast.Position;
				rotation = cast.Rotation;
			}
			var e = EntityObjectFactory.Instance.CreateEntityObject(state.Type, position, rotation);
			if ( e == null ) {
				return null;
			}
			setupAction?.Invoke(e);
			e.ApplyFullState(state);
			e.Create();
			_entities.Add(e.ID, e);
			return e;
		}

		public void DespawnEntity(uint uid) {
			var e = GetEntityById(uid);
			if ( _entities.ContainsKey(uid) ) {
				_entities.Remove(uid);
			}			
			if ( e ) {
				e.DestroyEntity();
			}			
		}

		public void UpdatePosition(uint uid, Vector3 pos, Vector3 rot, bool updatePosition, bool updateRotation) {
			var e = GetEntityById(uid);
			if ( e == null ) {
				return;
			}
			var st = (ISyncTransform)e;
			if ( st != null ) {
				if ( updateRotation ) {
					st.ApplyRotationUpdate(rot);
				}
				if ( updatePosition ) {
					st.ApplyPositionUpdate(pos);
				}
			}
		}

		public Entity GetEntityById(uint uid) {
			_entities.TryGetValue(uid, out var val);
			return val;
		}

		public void CallRPC(uint id, byte rpcId, byte[] args) {
			var e = GetEntityById(id);
			if ( e == null ) {
				return;
			}
			e.Client_CallRPC(rpcId, args);
		}

		public void UpdateSyncVar(uint id, byte varId, byte[] args) {
			var e = GetEntityById(id);
			if ( e == null ) {
				return;
			}
			e.Client_UpdateSyncVar(varId, args);
		}

		public void GetEntitiesOfType(EntityType type, List<Entity> outputList) {
			foreach ( var e in _entities ) {
				if ( e.Value.EntityType == type ) {
					outputList.Add(e.Value);
				}
			}
		}
	}
}
